package components
{
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.geom.Point;
	
	import oboe.core.Controller;
	import oboe.math.Number2D;
	import oboe.math.Number3D;
	
	public class Link extends Component
	{
		private var asset:Sprite;
		private var line:Shape;
		private var circle:Sprite;
		
		private var startPos:Number3D = new Number3D(0,0,0);
		private var endPos:Number3D = new Number3D(0,0,0);
		private var start:Circle;
		private var end:Circle;
		
		public var driftDist:Number = 0;
		public var driftAngle:Number = 0;
		private var nodeDist:Number;
		
		public function Link( start:Circle, end:Circle, data:Object=null )
		{
			this.start = start;
			this.end = end;

			super( this.asset = new Sprite(), data );
			
			this.asset.addChild( this.line = new Shape() );
			
			start.message("getOrbit", startPos);
			end.message("getOrbit", endPos);
			this.draw();
			
			this.setSelected(false);
		}
		
		public function getPerspectiveData():Object
		{
			throw "MUST OVERRIDE";
			return null;
		}
	
		public function setPerspectiveData(data:Object):void
		{
			throw "MUST OVERRIDE";
		}
		
		override protected function createData( data:Object ):ComponentData
		{
			return new LinkData( data, this.start, this.end );
		}
		
		[slot] public function onAddToParent():void
		{
			this.asset.parent.setChildIndex( this.asset, 1 );
		}
		
		public function messageStart( msg:String, ...a ):void
		{
			this.start.messageEx( msg, a );
		}
		
		public function messageEnd( msg:String, ...a ):void
		{
			this.end.messageEx( msg, a );
		}
		
		protected override function selfDestruct():void
		{
			start.message("removeLink", this);
			if( start != end ) end.message("removeLink", this);
			super.selfDestruct();
		}
		
		[slot] public function getPosition( p:Point ):void
		{
			p.x = this.circle.x;
			p.y = this.circle.y;
		}
		
		protected override function setPosition(x:Number, y:Number):void
		{
			t3d.copy2( this.startPos.x - this.endPos.x, this.startPos.y - this.endPos.y, 0 );
			tmpA.set( this.startPos.x + this.endPos.x, this.startPos.y + this.endPos.y ).mul(0.5);
			tmpB.set( x - tmpA.x, y - tmpA.y );
			this.driftAngle = Math.atan2( tmpB.x, tmpB.y ) - Math.atan2( t3d.x, t3d.y );
			this.driftDist = tmpB.length() / this.nodeDist;
			this.draw();
		}
		
		[slot] public function orbitChanged( node:Controller ):void
		{
			if( node == start ) start.message("getOrbit", startPos);
			if( node == end )   end.message("getOrbit", endPos);
			this.draw();
		}
		
		[slot] public override function setSelected(state:Boolean):void
		{
			super.setSelected(state);
			this.circle.alpha = Number(state)*0.7 + 0.3;
			this.circle.scaleX = this.circle.scaleY = Number(state)*0.5 + 1;
		}
		
		private static const tmpA:Number2D = new Number2D;
		private static const tmpB:Number2D = new Number2D;
		private static const t3d:Number3D = new Number3D(0,0,0);
		private function draw():void
		{
			if( this.start != this.end )
			{
				t3d.copy2( this.startPos.x - this.endPos.x, this.startPos.y - this.endPos.y, 0 );
				
				this.nodeDist = t3d.length();
			}
			else
			{
				this.nodeDist = 1;
			}
			
			if( !this.circle )
			{
				this.circle = new Sprite();
				this.circle.buttonMode = true;
				this.asset.addChild( this.circle );
				
				if( this.start == this.end )
				{
					this.driftDist = this.endPos.z * 1.3;
					this.driftAngle = Math.random()*Math.PI*2;
				}

				with( this.circle.graphics )
				{
					clear();
					beginFill(0xFFFF00);
					drawCircle(0, 0, 5);
				}
			}

			var lineAngle:Number = Math.atan2( t3d.x, t3d.y );
			
			tmpA.set( this.startPos.x + this.endPos.x, this.startPos.y + this.endPos.y ).mul(0.5);
			this.circle.x = tmpA.x + Math.sin( lineAngle + this.driftAngle )*this.driftDist*this.nodeDist;
			this.circle.y = tmpA.y + Math.cos( lineAngle + this.driftAngle )*this.driftDist*this.nodeDist;

			tmpA.set( this.circle.x-this.endPos.x, this.circle.y-this.endPos.y );
			tmpB.set( this.circle.x-this.startPos.x, this.circle.y-this.startPos.y );
			var toEnd:Number = (tmpA.length() + tmpB.length() - endPos.z);
			var toStart:Number = (tmpA.length() + tmpB.length());
			
			var a:Number = Math.atan2( this.startPos.x-this.circle.x, this.startPos.y-this.circle.y );
			tmpB.set( this.startPos.x - Math.sin( a )*toEnd, this.startPos.y - Math.cos( a )*toEnd );
			a = Math.atan2( this.endPos.x-this.circle.x, this.endPos.y-this.circle.y );
			tmpA.set( this.endPos.x - Math.sin( a )*toStart, this.endPos.y - Math.cos( a )*toStart );
			
			tmpA.x = (tmpA.x+this.startPos.x)*0.5;
			tmpA.y = (tmpA.y+this.startPos.y)*0.5;
			
			a = Math.atan2( this.endPos.x-this.circle.x, this.endPos.y-this.circle.y );
			t3d.copy2( this.endPos.x - Math.sin( a )*this.endPos.z, this.endPos.y - Math.cos( a )*this.endPos.z, 0 );
			
			tmpB.x = (tmpB.x+t3d.x)*0.5;
			tmpB.y = (tmpB.y+t3d.y)*0.5;
			
			with( this.line.graphics )
			{
				clear();
				lineStyle(1);
				moveTo( this.startPos.x, this.startPos.y );
				curveTo( tmpA.x, tmpA.y, circle.x, circle.y );
				curveTo( tmpB.x, tmpB.y, t3d.x, t3d.y );
				beginFill(0);
				drawCircle( t3d.x, t3d.y, 6 );
				endFill();
			}
		}
	}
}
